package com.zf.openfeign.caller.config;

import feign.Client;
import feign.RequestInterceptor;
import feign.Target;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.openfeign.loadbalancer.FeignLoadBalancerAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;

import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * 本地feign调用：不再使用服务发现和负载均衡，手动将服务名替换成指定的 url
 */
@ConditionalOnProperty(prefix = "local-call.feign", name = "enable", havingValue = "true", matchIfMissing = false)
@AutoConfigureBefore(FeignLoadBalancerAutoConfiguration.class)
@Configuration
public class FeignLocalCallConfig {

    private static final Logger log = LoggerFactory.getLogger(FeignLocalCallConfig.class);

    @Value("#{${local-call.feign.service-map}}")
    private Map<String, String> serviceMap;


    /**
     * 定义一个普通的 Client, 不使用负载均衡 Client
     */
    @Bean
    public Client client() {
        HashMap<String, String> tempMap = new HashMap<>();
        for (Map.Entry<String, String> entry : Optional.ofNullable(serviceMap).orElse(Collections.emptyMap()).entrySet()) {
            String path = entry.getValue();
            // 去除结尾的斜线
            if (path.endsWith("/")) {
                tempMap.put(entry.getKey(), path.substring(0, path.length() - 1));
            } else {
                tempMap.put(entry.getKey(), path);
            }
        }
        serviceMap = tempMap;
        log.info("本地Feign调用映射已启用");
        return new Client.Default(null, null);
    }


    /**
     * 定义一个拦截器，根据配置将服务名映射成指定的url
     */
    @Bean
    public RequestInterceptor localServiceMapInterceptor() {
        return requestTemplate -> {
            Target<?> target = requestTemplate.feignTarget();
            // 获取服务名
            String serviceName = target.name();
            // 根据服务名获取本地配置的地址
            String localServiceAddr = Optional.ofNullable(serviceMap.get(serviceName)).orElse("");
            Assert.hasText(localServiceAddr, String.format("服务 '%s' 没有配置本地映射", serviceName));

            // 如果不是 http:// 或 https:// 开头，则报错
            if (!localServiceAddr.startsWith("http://") && !localServiceAddr.startsWith("https://")) {
                Assert.hasText(null, String.format("服务 '%s' 配置的本地映射 '%s' 必须以 http:// 或 https:// 开头", serviceName, localServiceAddr));
            }

            // 拼接成新的url
            URI originalTargetUri = URI.create(target.url());
            String newUrl = localServiceAddr + originalTargetUri.getPath();

            log.info("Feign调用地址映射成功，原服务地址 '{}' 被映射成 '{}'", target.url() + requestTemplate.url(), newUrl + requestTemplate.url());

            // 设置新的url到 RequestTemplate 中，用于生成最终的调用地址
            requestTemplate.target(newUrl);

            // // 只修改了请求路径，没有改动请求体，不需要移除 Content-Length 头
            // requestTemplate.removeHeader(HttpHeaders.CONTENT_LENGTH);
            // requestTemplate.header(HttpHeaders.TRANSFER_ENCODING, "chunked");

            // 设置一些其他的头
            // requestTemplate.header(HttpHeaders.AUTHORIZATION, "token");
        };
    }


}
